/*
 * $Id$
 *
 * Copyright (c) 2006, 2006 WorldTicket A/S
 * All rights reserved.
 */
package my.crawler.util;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathVariableResolver;

import my.news.crawler.URLContentProvider;

import org.cyberneko.html.parsers.DOMParser;
import org.cyberneko.html.parsers.SAXParser;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * @author Yuriy V. Koroliov (YUK) / WorldTicket A/S
 * @version $Revision$ $Date$
 */
public class XMLUtil {
    static Transformer xformer;
    static XPath xpath;
    static {

        try {
            xformer = TransformerFactory.newInstance().newTransformer();
            xformer.setOutputProperty("encoding", "iso-8859-1");
            xformer.setOutputProperty("indent", "yes");
            xformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount",   "4");

            XPathFactory factory = XPathFactory.newInstance();

            xpath = factory.newXPath();
            xpath.setNamespaceContext(new PersonalNamespaceContext());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public static Document buildDOM(InputStream io) throws Exception {
        DOMParser parser = new DOMParser();
        parser.parse(new InputSource(io));
        io.close();
        return parser.getDocument();
    }

    public interface ContentListener {
        public void endElement(String arg0, String arg1, String arg2)
        throws SAXException;
        public void startElement(String arg0, String arg1, String arg2,
                Attributes arg3) throws SAXException;
        public void processText(String text) throws SAXException;
    }

    public static void parseSAX(InputStream io, final ContentListener listener) throws Exception {
        SAXParser parser = new SAXParser();
        parser.setContentHandler(new DefaultHandler(){

            public void characters(char[] arg0, int arg1, int arg2)
                    throws SAXException {
                String content;
                content = new String(arg0, arg1, arg2);
                listener.processText(content);
            }

            public void endElement(String arg0, String arg1, String arg2)
                    throws SAXException {
                listener.endElement(arg0, arg1, arg2);
            }

            public void startElement(String arg0, String arg1, String arg2,
                    Attributes arg3) throws SAXException {
                listener.startElement(arg0, arg1, arg2, arg3);
            }

        });
        parser.parse(new InputSource(io));
        io.close();
    }

    public static List<Node> wrap(NodeList result) {
        List<Node> nodes = new ArrayList<Node>();
        for (int i = 0; i < result.getLength(); i++) {
            nodes.add(result.item(i));
        }
        return nodes;
    }

    public static String toXMLString(Document doc) throws Exception {
        StringWriter out = new StringWriter();
        StreamResult result = new StreamResult(out);
        xformer.transform(new DOMSource(doc), result);
        return result.getWriter().toString();

    }

    public static List<Node> getNodesWithText(Document doc, String text)
            throws Exception {
        XPathExpression expr = xpath.compile("//.");
        NodeList result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
        return wrap(result);
    }

    public static List<Node> getNodes(Document doc, String xapth)
            throws Exception {
        XPathExpression expr = xpath.compile(xapth);
        NodeList result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
        List<Node> nodes = new ArrayList<Node>(result.getLength());
        for (int i = 0; i < result.getLength(); i++) {
            nodes.add(result.item(i));
        }
        return nodes;
    }

    public static Document buildDOM(URL current) throws IOException, Exception {
        return buildDOM(XMLUtil.openStream(current));
    }

    public static InputStream openStream(URL url) throws Exception {
        return provider.get(url);
    }

    public static final URLContentProvider DEFUALT_PROVIDER = new URLContentProvider() {

        public InputStream get(URL url) throws Exception {
            return url.openStream();
        }

    };

    public static URLContentProvider provider = DEFUALT_PROVIDER;

    public static String getXPath(Node n) {
        StringBuilder xpath = new StringBuilder();
        while (n.getParentNode() != null) {
            xpath.insert(0, "/"+n.getNodeName());
            n = n.getParentNode();
        }
        return xpath.toString();
    }

    public static class PersonalNamespaceContext implements NamespaceContext {

        public String getNamespaceURI(String prefix) {
            if (prefix == null)
                throw new NullPointerException("Null prefix");
            else if ("html".equals(prefix))
                return "http://www.w3.org/1999/xhtml";
            else if ("xml".equals(prefix))
                return XMLConstants.XML_NS_URI;
            return XMLConstants.NULL_NS_URI;
        }

        // This method isn't necessary for XPath processing.
        public String getPrefix(String uri) {
            throw new UnsupportedOperationException();
        }

        // This method isn't necessary for XPath processing either.
        public Iterator getPrefixes(String uri) {
            throw new UnsupportedOperationException();
        }

    }

    public static Document buildDOM(String string) throws Exception {
        return buildDOM(new ByteArrayInputStream(string.getBytes()));
    }
}
